﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// 
    /// </summary>
    public class CNewImageFilters : BaseImageFilter
    {
        private ConvolutionMatrixCustom m_CustomMatrix = null;

        /// <summary>
        /// Initializes a new instance of the <see cref="CNewImageFilters"/> class.
        /// </summary>
        public CNewImageFilters()
            : this(ConvolutionMatrixCustom.Sobel())
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CNewImageFilters"/> class.
        /// </summary>
        /// <param name="_customMatrix">The _custom matrix.</param>
        public CNewImageFilters(ConvolutionMatrixCustom _customMatrix)
        {
            m_CustomMatrix = _customMatrix;
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            Convolution(m_CustomMatrix, _bitmap);
            return _bitmap;
        }

        /// <summary>
        /// Applies the convolution.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <returns></returns>
        public int ApplyConvolution(UnsafeBitmap input,int x, int y)
        {
            int nLength = (int)Math.Sqrt(this.m_CustomMatrix.MatrixValues.LongLength);
            int kernelWidth = nLength; 
            int kernelHeight = nLength; 
            int output = 0;
            for (int i = 0; i < kernelWidth; ++i)
            {
                for (int j = 0; j < kernelHeight; ++j)
                {
                    output = output + input[x + i,y + j].ToArgb() * this.m_CustomMatrix.MatrixValues[i,j];
                }
            }
            return output;
        }

        /// <summary>
        /// Convolutions the specified _matrix.
        /// </summary>
        /// <param name="_matrix">The _matrix.</param>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <returns></returns>
        private static bool Convolution(ConvolutionMatrixCustom _matrix, UnsafeBitmap _bitmap)
        {
            UnsafeBitmap bitmap = _bitmap;
            int[,] m_Kernel = _matrix.MatrixValues;

            int m_ySize = _bitmap.Height;
            int m_xSize = _bitmap.Width;

            int[,] m_i0 = new int[m_ySize, m_xSize];

            int m_nY = 0;
            int m_nX = 0;

            int _nZ = _matrix.MatrixValues.Length;

            int _nZTemp = (int)Math.Sqrt(_nZ);
            if (_nZTemp != 3 & _nZTemp != 5)
            {
                bitmap.Dispose();
                return false;
            }
            _nZ = (_nZ - 1) / 2;
            Single[,] help = new Single[m_ySize, m_xSize];
            Single gray, factor, min = Single.MaxValue, max = Single.MinValue;
            Int32 xx, yy, xxx, yyy, weight, sum, divisor;
            for (m_nY = 0; m_nY < m_ySize; m_nY++)
                for (m_nX = 0; m_nX < m_xSize; m_nX++)
                {
                    sum = divisor = 0;
                    for (yy = 0; yy <= 2 * _nZ; yy++)
                    {
                        yyy = m_nY + yy - _nZ;
                        if (yyy < 0 | yyy >= m_ySize) continue;
                        for (xx = 0; xx <= 2 * _nZ; xx++)
                        {
                            xxx = m_nX + xx - _nZ;
                            if (xxx < 0 | xxx >= m_xSize) continue;
                            weight = bitmap.GetPixel(m_nX, m_nY).ToArgb();
                            sum += bitmap.GetPixel(m_nX,m_nY).ToArgb() * weight;
                            divisor += weight;
                        }
                    }
                    if (divisor <= 1)
                    {
                        gray = (Single)sum;
                    }
                    else
                    {
                        gray = (Single)sum / (Single)divisor;
                    }
                    if (gray > max)
                    {
                        max = gray;
                    }
                    if (gray < min)
                    {
                        min = gray;
                    }
                    help[m_nY, m_nX] = gray;
                }
            factor = 9.0f / (max - min);
            for (m_nY = 0; m_nY < m_ySize; m_nY++)
            {
                for (m_nX = 0; m_nX < m_xSize; m_nX++)
                {
                    bitmap.SetPixel(m_nX, m_nY, Color.FromArgb((Byte)(factor * (help[m_nY, m_nX] - min))));
                }
            }
            bitmap.Dispose();
            return true;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new CNewImageFilters(this.m_CustomMatrix);
        }
    }
}
